package com.atguigu.gmall.pms.service.impl;

import com.atguigu.gmall.common.consts.GmallConsts;
import com.atguigu.gmall.pms.entity.*;
import com.atguigu.gmall.pms.feign.GmallSmsClient;
import com.atguigu.gmall.pms.service.*;
import com.atguigu.gmall.pms.vo.SkuVo;
import com.atguigu.gmall.pms.vo.SpuAttrValueVo;
import com.atguigu.gmall.pms.vo.SpuVo;
import com.atguigu.gmall.sms.vo.SkuSaleInfoVo;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.gmall.common.bean.PageResultVo;
import com.atguigu.gmall.common.bean.PageParamVo;

import com.atguigu.gmall.pms.mapper.SpuMapper;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;


@Service("spuService")
@Transactional
public class SpuServiceImpl extends ServiceImpl<SpuMapper, SpuEntity> implements SpuService {

    @Override
    public PageResultVo queryPage(PageParamVo paramVo) {
        IPage<SpuEntity> page = this.page(
                paramVo.getPage(),
                new QueryWrapper<SpuEntity>()
        );

        return new PageResultVo(page);
    }

    @Override
    public PageResultVo querySpupageByCondition(Long categoryId, PageParamVo paramVo) {
        LambdaQueryWrapper<SpuEntity> queryWrapper = new LambdaQueryWrapper<>();
        if(categoryId!=0){
            //使用categoryid过滤数据
            queryWrapper.eq(SpuEntity::getCategoryId , categoryId);
        }
        String key = paramVo.getKey();
        //http://api.gmall.com/pms/spu/category/225?t=1650426418141&key=华为
        if(StringUtils.isNotBlank(key)){
            // WHERE category_id = 225 AND (id LIKE '%7%' OR NAME LIKE '%7%');
            queryWrapper.and(qw->qw.like(SpuEntity::getId , key).or().like(SpuEntity::getName , key) );
        }

        IPage<SpuEntity> page = this.page(paramVo.getPage(),queryWrapper);//参数1：分页页码和pageSize，参数2：查询条件
        return new PageResultVo(page);
    }
    @Autowired
    SpuDescService spuDescService;
    @Autowired
    SpuAttrValueService spuAttrValueService;
    @Autowired
    SkuService skuService;
    @Autowired
    SkuImagesService skuImagesService;
    @Autowired
    SkuAttrValueService skuAttrValueService;
    @Autowired
    GmallSmsClient gmallSmsClient;
    /*

        本地事务：一个逻辑单元所有的sql操作是针对一个数据库的操作(Connection对象：conn.setAutocommit(false))
             事务：一组sql操作，我们希望他们作为一个完整的逻辑单元，要么一起成功 要么一起失败
             A:原子性
             C:一致性
             I:隔离性
             D:持久性
             脏读： 一个事务可以读取另一个事务未提交的数据
             不可重复读： 一个事务开始执行时和执行中读取同一行数据的结果不一致
             幻读：一个事务开始执行时读取的行数和执行中再次读取的行数多了或者少了
             事务隔离级别：可以解决以上的问题，本质是通过排它锁来实现的，所以会影响sql执行的效率
                READ UNCOMMITED : 读未提交(一个事务可以读取其他事务未提交的数据，一般只会读的数据库可以设置该隔离级别)
                READ COMMITED: 读已提交(一个事务只能读取另一个事务提交的结果，解决了脏读问题)
                REPEATABLE READ: 可重复读(mysql默认的事务隔离级别)
                SERIALIZABLE: 串行化(可以解决幻读问题)
     */
    @GlobalTransactional  //使用动态代理的方式代理业务类对象，动态代理的invoke方法对当前的业务方法执行的前后进行了扩展
    @Override
    public void saveSpuVo(SpuVo spuVo) {
//        System.out.println(spuVo);
        //1、保存spu数据到spu表中 获取spuid
        //将spuVo的属性值拷贝给spuEntity对象
        Long spuId = saveSpuEntity(spuVo);//回滚
        //2、保存spu的图片集合
        spuDescService.saveSpuImages(spuVo, spuId);//不使用saveSpuVo的事务，自己新创建一个使用 不受saveSpuVo事务控制
        //3、保存spu的基本属性值
        spuAttrValueService.saveSpuAttrValue(spuVo, spuId);
        //4、保存sku集合
        skuService.saveSkus(spuVo, spuId);
        //5、发送消息到消息队列
        //参数1：交换机名称  参数2：路由key  参数3:消息  spuId
        //多个项目都需要使用的固定字符串抽取为常量字符串
        rabbitTemplate.convertAndSend(GmallConsts.SAVE_SPU_EXCHANGE_NAME,
                GmallConsts.SAVE_SPU_ROUTING_KEY , spuId);

    }
    @Autowired
    RabbitTemplate rabbitTemplate;
    /*
        本地事务：
        分布式事务：

     */


    //1、保存spu的方法
    public Long saveSpuEntity(SpuVo spuVo) {
        SpuEntity spuEntity = new SpuEntity();
        BeanUtils.copyProperties(spuVo,spuEntity);
        spuEntity.setCreateTime(new Date());
        spuEntity.setUpdateTime(new Date());
        this.save(spuEntity);
        return spuEntity.getId();
    }

}